﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Echo.Server
{
    using System;
    using System.IO;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading.Tasks;
    using DotNetty.Codecs;
    using DotNetty.Handlers.Logging;
    using DotNetty.Handlers.Tls;
    using DotNetty.Transport.Bootstrapping;
    using DotNetty.Transport.Channels;
    using DotNetty.Transport.Channels.Sockets;
    using DotNetty.Transport.Libuv;
    using Examples.Common;

    class Program
    {
        static async Task RunServerAsync()
        {
            // DotNetty运行时打印一些框架内置的日志出来(在生产环境该日志可以选择关闭,提高性能,打开是为了排查问题)
            ExampleHelper.SetConsoleLogger();
            // 主要工作组
            IEventLoopGroup bossGroup;
            // 子工作组,推荐设置为内核数*2的线程数(比如8核这里就开16个线程)
            IEventLoopGroup workerGroup;


            // Libuv:是DotNetty本身实现基于Libuv(高性能的,事件驱动的I/O库) 核心的实现
            if (ServerSettings.UseLibuv)
            {
                var dispatcher = new DispatcherEventLoopGroup();
                bossGroup = dispatcher;
                workerGroup = new WorkerEventLoopGroup(dispatcher);
            }
            else
            {
                // 主线程只会实例化一个
                bossGroup = new MultithreadEventLoopGroup(1);
                // 子线程组可以按照自己的需求在构造函数里指定数量
                workerGroup = new MultithreadEventLoopGroup();
            }

            X509Certificate2 tlsCertificate = null;
            // 是否使用ssl套接字加密
            if (ServerSettings.IsSsl)
            {
                tlsCertificate = new X509Certificate2(Path.Combine(ExampleHelper.ProcessDirectory, "dotnetty.com.pfx"), "password");
            }
            try
            {
                /*
                    *ServerBootstrap是一个引导类，表示实例化的是一个服务端对象
                    *声明一个服务端Bootstrap，每个Netty服务端程序，都由ServerBootstrap控制，
                    *通过链式的方式组装需要的参数
                    */
                // ServerBootstrap:相当于对System.Net.Sockets.Socket对象的封装
                var bootstrap = new ServerBootstrap();
                bootstrap.Group(bossGroup, workerGroup);

                if (ServerSettings.UseLibuv)
                {
                    bootstrap.Channel<TcpServerChannel>();
                }
                else
                {
                    bootstrap.Channel<TcpServerSocketChannel>();
                }

                bootstrap
                    .Option(ChannelOption.SoBacklog, 100)
                    .Handler(new LoggingHandler("SRV-LSTN"))
                    .ChildHandler(new ActionChannelInitializer<IChannel>(channel =>
                    {
                        /*
                        * 这里主要是配置channel中需要被设置哪些参数，以及channel具体的实现方法内容。
                        * channel可以理解为，socket通讯当中客户端和服务端的连接会话，会话内容的处理在channel中实现。
                        */
                        IChannelPipeline pipeline = channel.Pipeline;
                        if (tlsCertificate != null)
                        {
                            pipeline.AddLast("tls", TlsHandler.Server(tlsCertificate)); //添加ssl加密
                        }
                        pipeline.AddLast(new LoggingHandler("SRV-CONN"));
                        pipeline.AddLast("framing-enc", new LengthFieldPrepender(2)); //Dotnetty自带的编码器，将要发送的内容进行编码然后发送
                        pipeline.AddLast("framing-dec", new LengthFieldBasedFrameDecoder(ushort.MaxValue, 0, 2, 0, 2)); ;//Dotnetty自带的解码器，将接受到的内容进行解码然后根据内容对应到业务逻辑当中

                        pipeline.AddLast("echo", new EchoServerHandler());
                    }));

                //指定服务端的端口号，ip地址donetty可以自动获取到本机的地址。也可以在这里手动指定
                IChannel boundChannel = await bootstrap.BindAsync(ServerSettings.Port);

                Console.ReadLine();

                await boundChannel.CloseAsync();
            }
            finally
            {
                //关闭释放并退出
                await Task.WhenAll(
                    bossGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)),
                    workerGroup.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)));
            }
        }

        static void Main() => RunServerAsync().Wait();
    }
}